package com.lucanjun.cn.springboot.common.utils;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * @author lucanjun
 * @version 1.0.0
 * @title AES256Util
 * @create 2024-12-19 16:55:19
 * @description AES256加密工具类
 */
@Slf4j
@Component
public class AES256Util {

    /**
     * 1、简介
     * AES，全称为 Advanced Encryption Standard，是一种分组密码算法，用于保护敏感数据的传输和存储。
     * AES 分为 128 位和 256 位两种密钥长度，可以对数据进行加密和解密，保证数据的安全性和完整性。
     * AES 主要应用于电子商务、移动支付、网络安全等领域，被广泛运用于现代社会的各个方面。
     * AES 算法被设计为高度安全，可以在理论上保证其分组密码的安全性。
     * 然而，由于其复杂性和密钥长度，AES 算法的实现和应用也具有一定的技术难度。
     * 因此，在应用 AES算法时，需要注意加强密钥管理和安全性保障。
     * 这个标准用来替代原先的 DES（Data Encryption Standard），已经被多方分析且广为全世界所使用。
     * AES 算法具有很多优点，例如快速、安全、可靠等。它可以加密大量数据，而不会因为加密过程中的数据量过大而变得缓慢。
     * 此外，AES 算法还支持块大小的自动调整，可以处理不同大小的数据块。
     * 2、AES 加密模式
     * 2.1、加密方式
     * ECB（Electronic Codebook）模式：
     * 这种模式是将整个明文分成若干段相同的小段，然后对每一小段进行加密。加密时，使用一个密钥，将明文中的每个字符与密钥中对应位置的字符进行异或运算，得到密文。
     * CBC（Cipher Block Chaining）模式：
     * 这种模式是先将明文切分成若干小段，然后每一小段与初始块或者上一段的密文段进行异或运算后，再与密钥进行加密。加密时，使用一个密钥和一个初始化向量（IV），初始化向量是一个16字节的向量，包含了加密算法所需的所有信息。
     * CFB（Cipher Feedback）模式：
     * 这种模式是一种较为复杂的加密模式，它结合了CBC和CTR两种模式的优点。在CFB模式中，加密过程中使用一个密钥和一个随机生成的初始化向量（IV），然后对明文进行加密。在加密完成后，通过对明文进行非对称加密来生成密文的向量。随后，通过对密文进行反向操作，将密文的向量与明文的向量进行异或运算，得到解密所需的密钥。
     * 需要注意的是，ECB、CBC、CFB等模式都是对称加密算法，加密和解密使用相同的密钥。在使用这些算法时，需要注意保护密钥的安全，避免被恶意获取。
     * 2.2、安全性
     * ECB 不够安全，只适合于短数据的加密，而 CBC 和 CFB 相较于 ECB 更加安全，因为前一个密文块会影响当前明文块，使攻击者难以预测密文的结构。
     * 2.3、速度
     * ECB 是最简单的加密方式，速度最快，但由于安全性差不建议使用，CBC 因为每个明文块都要与前一个密文块进行异或操作，比 ECB 要慢一些，CFB 因为需要反复加密和解密，速度可能会更慢。
     * 总的来说，选择 AES 的算法模式需要根据加密需要的安全性和速度来进行选择，通常推荐使用CBC 或 CFB 模式，而不是 ECB 模式。
     */

    /**
     * 加密模式之 ECB，算法/模式/补码方式
     */
    //private static final String AES_ECB = "AES/ECB/PKCS5Padding";

    private static String AES_ECB;

    @Value("${aes.ecb}")
    public void setAesEcb(String aesEcb) {
        AES256Util.AES_ECB = aesEcb;
    }

    /**
     * 加密模式之 CBC，算法/模式/补码方式
     */
    //private static final String AES_CBC = "AES/CBC/PKCS5Padding";

    private static String AES_CBC;

    @Value("${aes.cbc}")
    public void setAesCcb(String aesCcb) {
        AES256Util.AES_CBC = aesCcb;
    }

    /**
     * 加密模式之 CFB，算法/模式/补码方式
     */
    //private static final String AES_CFB = "AES/CFB/PKCS5Padding";

    private static String AES_CFB;

    @Value("${aes.cfb}")
    public void setAesCfb(String aesCfb) {
        AES256Util.AES_CFB = aesCfb;
    }

    /**
     * AES 中的 IV 必须是 16 字节（128位）长
     */
    private static final Integer IV_LENGTH = 16;

    /**
     * AES 中的 向量
     */
    //private static final String iv = getIV();
    //private static final String iv = "qdfghjg145df45uk";
    public static String iv;

    @Value("${aes.iv}")
    public void setIv(String iv) {
        AES256Util.iv = iv;
    }

    /**
     * AES 中的 密钥 32字节（AES128-16字节）
     */
    //private static final String key = "uBdUx82vPHkDKb284d7NkjFoNcKWBuka";
    public static String key;

    @Value("${aes.key}")
    public void setKey(String key) {
        AES256Util.key = key;
    }

    /**
     * <h2>加密 - 模式 ECB</h2>
     *
     * @param text 需要加密的文本内容
     */
    public static String encryptECB(String text) {
        log.info("EBC 加密前内容：{}", JSON.toJSON(text));
        //校验
        if (StringUtils.isBlank(text) || StringUtils.isBlank(key)) {
            return null;
        }

        try {
            // 创建AES加密器
            Cipher cipher = Cipher.getInstance(AES_ECB);

            SecretKeySpec secretKeySpec = getSecretKeySpec(key);

            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

            byte[] textBytes = text.getBytes(StandardCharsets.UTF_8);

            // 加密字节数组
            byte[] encryptedBytes = cipher.doFinal(textBytes);

            // 将密文转换为 Base64 编码字符串
            String encode = Base64.getEncoder().encodeToString(encryptedBytes);
            log.info("EBC 加密后内容：{}", JSON.toJSON(encode));
            return encode;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * <h2>解密 - 模式 ECB</h2>
     *
     * @param text 需要解密的文本内容
     */
    public static String decryptECB(String text) {
        log.info("EBC 解密前内容：{}", JSON.toJSON(text));
        //校验
        if (StringUtils.isBlank(text) || StringUtils.isBlank(key)) {
            return null;
        }

        // 将密文转换为16字节的字节数组
        byte[] textBytes = Base64.getDecoder().decode(text);

        try {
            // 创建AES加密器
            Cipher cipher = Cipher.getInstance(AES_ECB);

            SecretKeySpec secretKeySpec = getSecretKeySpec(key);

            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);

            // 解密字节数组
            byte[] decryptedBytes = cipher.doFinal(textBytes);

            // 将明文转换为字符串
            String decrypt = new String(decryptedBytes, StandardCharsets.UTF_8);
            log.info("EBC 解密后内容：{}", JSON.toJSON(decrypt));
            return decrypt;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /***
     * <h2>初始化向量（IV），它是一个随机生成的字节数组，用于增加加密和解密的安全性</h2>
     */
//    public static String getIV() {
//        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
//        Random random = new Random();
//        StringBuffer sb = new StringBuffer();
//        for (int i = 0; i < IV_LENGTH; i++) {
//            int number = random.nextInt(str.length());
//            sb.append(str.charAt(number));
//        }
//        return sb.toString();
//    }

    /***
     * <h2>获取一个 AES 密钥规范</h2>
     */
    public static SecretKeySpec getSecretKeySpec(String key) {
        //校验
        if (StringUtils.isBlank(key)) {
            return null;
        }
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
        return secretKeySpec;
    }

    /**
     * <h2>加密 - 模式 CBC</h2>
     *
     * @param text 需要加密的文本内容
     */
    public static String encryptCBC(String text) {
        log.info("CBC 加密前内容：{}", JSON.toJSON(text));
        //校验
        if (StringUtils.isBlank(text) || StringUtils.isBlank(key) || StringUtils.isBlank(iv)) {
            return null;
        }

        try {
            // 创建AES加密器
            Cipher cipher = Cipher.getInstance(AES_CBC);

            SecretKeySpec secretKeySpec = getSecretKeySpec(key);

            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8)));

            // 加密字节数组
            byte[] encryptedBytes = cipher.doFinal(text.getBytes(StandardCharsets.UTF_8));

            // 将密文转换为 Base64 编码字符串
            String encode = Base64.getEncoder().encodeToString(encryptedBytes);
            log.info("CBC 加密后内容：{}", JSON.toJSON(encode));
            return encode;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * <h2>解密 - 模式 CBC</h2>
     *
     * @param text 需要解密的文本内容
     */
    public static String decryptCBC(String text) {
        log.info("CBC 解密前内容：{}", JSON.toJSON(text));
        //校验
        if (StringUtils.isBlank(text) || StringUtils.isBlank(key) || StringUtils.isBlank(iv)) {
            return null;
        }

        // 将密文转换为16字节的字节数组
        byte[] textBytes = Base64.getDecoder().decode(text);

        try {
            // 创建AES加密器
            Cipher cipher = Cipher.getInstance(AES_CBC);

            SecretKeySpec secretKeySpec = getSecretKeySpec(key);

            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8)));

            // 解密字节数组
            byte[] decryptedBytes = cipher.doFinal(textBytes);

            // 将明文转换为字符串
            String decrypt = new String(decryptedBytes, StandardCharsets.UTF_8);
            log.info("CBC 解密后内容：{}", JSON.toJSON(decrypt));
            return decrypt;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * <h2>加密 - 模式 CFB</h2>
     *
     * @param text 需要加密的文本内容
     */
    public static String encryptCFB(String text) {
        log.info("CFB 加密前内容：{}", JSON.toJSON(text));
        //校验
        if (StringUtils.isBlank(text) || StringUtils.isBlank(key) || StringUtils.isBlank(iv)) {
            return null;
        }

        try {
            // 创建AES加密器
            Cipher cipher = Cipher.getInstance(AES_CFB);

            SecretKeySpec secretKeySpec = getSecretKeySpec(key);

            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8)));

            // 加密字节数组
            byte[] encryptedBytes = cipher.doFinal(text.getBytes(StandardCharsets.UTF_8));

            // 将密文转换为 Base64 编码字符串
            String encode = Base64.getEncoder().encodeToString(encryptedBytes);
            log.info("CFB 加密后内容：{}", JSON.toJSON(encode));
            return encode;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * <h2>解密 - 模式 CFB</h2>
     *
     * @param text 需要解密的文本内容
     */
    public static String decryptCFB(String text) {
        log.info("CFB 解密前内容：{}", JSON.toJSON(text));
        //校验
        if (StringUtils.isBlank(text) || StringUtils.isBlank(key) || StringUtils.isBlank(iv)) {
            return null;
        }

        // 将密文转换为16字节的字节数组
        byte[] textBytes = Base64.getDecoder().decode(text);

        try {
            // 创建AES加密器
            Cipher cipher = Cipher.getInstance(AES_CFB);

            SecretKeySpec secretKeySpec = getSecretKeySpec(key);

            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8)));

            // 解密字节数组
            byte[] decryptedBytes = cipher.doFinal(textBytes);

            // 将明文转换为字符串
            String decrypt = new String(decryptedBytes, StandardCharsets.UTF_8);
            log.info("CFB 解密后内容：{}", JSON.toJSON(decrypt));
            return decrypt;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        String text = "嗨，您好！";

        String encryptTextEBC = encryptECB(text);
        log.info("EBC 加密前内容：{}", text);
        log.info("EBC 加密后内容：{}", encryptTextEBC);
        log.info("EBC 解密后内容：{}", AES256Util.decryptECB(encryptTextEBC));
        log.info("----------------------------------------------------");

        String encryptTextCBC = encryptCBC(text);
        log.info("CBC 加密IV：{}", AES256Util.iv);
        log.info("CBC 加密前内容：{}", text);
        log.info("CBC 加密后内容：{}", encryptTextCBC);
        log.info("CBC 解密后内容：{}", AES256Util.decryptCBC(encryptTextCBC));
        log.info("----------------------------------------------------");

        String encryptTextCFB = encryptCFB(text);
        log.info("CFB 加密IV：{}", AES256Util.iv);
        log.info("CFB 加密前内容：{}", text);
        log.info("CFB 加密后内容：{}", encryptTextCFB);
        log.info("CFB 解密后内容：{}", AES256Util.decryptCFB(encryptTextCFB));

    }

}
